package com.hyq.grid.algorithm;

import com.hyq.grid.common.File_operation;
import com.hyq.grid.common.gdalTool;
import com.hyq.grid.common.tools;

import java.awt.geom.Point2D;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class TransformAlgo {

    private final static String gmshPath = "D:\\gmsh-4.6.0-Windows64\\";

    public static Map<String, Object> DemtoHexagon(String DemPath) {

        Map demInfor = gdalTool.getDemInf(DemPath);
        List<Double[]> node = (List<Double[]>) demInfor.get("node");
        if (node == null) {
            System.out.println("读取DEM点信息失败！！");
            return null;
        }
        List<Double[]> border = (List<Double[]>) demInfor.get("border");
        if (border == null) {
            System.out.println("读取DEM边界信息失败！！");
            return null;
        }
        List<Map> mapBorder = new ArrayList<>();
        Double minX = border.get(0)[0];
        Double minY = border.get(0)[1];
        for (Double[] elementbord : border) {
            Map<String, Double> tempBorderP = new HashMap<>();
            if (minX > elementbord[0]) minX = elementbord[0];
            if (minY > elementbord[1]) minY = elementbord[1];
            tempBorderP.put("X", elementbord[0]);
            tempBorderP.put("Y", elementbord[1]);
            mapBorder.add(tempBorderP);
        }

//        六边形填充
        double rasterLen = node.get(1)[0] - node.get(0)[0];
        double cellLen = rasterLen * 2 / 3;
        MyAlgo myAlgo = new MyAlgo(mapBorder, cellLen);
        Map outPutData = myAlgo.hexagon();
        List<Map> hexagon = (List<Map>) outPutData.get("element");
        int XSize = (int) demInfor.get("xsize");
        int YSize = (int) demInfor.get("ysize");
        List<Double> heightList = new ArrayList<>();
        //        DEM范围：MinX,MaxX,MinY,MaxY,MinZ,MaxZ
        Double[] demRange = new Double[]{1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0};
        for (int i = 0; i < hexagon.size(); i++) {

            Map hexagoncoor = hexagon.get(i);
            Double[] X = (Double[]) hexagoncoor.get("X");
            Double[] Y = (Double[]) hexagoncoor.get("Y");

            Double centerX = (tools.MaxofArray(X) + tools.MinofArray(X)) / 2;
            Double centerY = (tools.MaxofArray(Y) + tools.MinofArray(Y)) / 2;

            int xLength = (int) Math.floor((centerX - minX) / rasterLen);
            int yLength = (int) Math.floor((centerY - minY) / rasterLen);

//            当前六边形
            List<Point2D.Double> currentHexagon = new ArrayList<>();
            for (int j = 0; j < X.length; j++) {
                currentHexagon.add(new Point2D.Double(X[j], Y[j]));

//                获取最小最大的x，y
                if (X[j] < demRange[0]) demRange[0] = X[j];
                if (X[j] > demRange[1]) demRange[1] = X[j];

                if (Y[j] < demRange[2]) demRange[2] = Y[j];
                if (Y[j] > demRange[3]) demRange[3] = Y[j];
            }

            List<Double[]> neiPoints = new ArrayList<>();
            neiPoints.add(node.get((yLength) * XSize + xLength));
            if ((yLength * XSize + xLength - 1) >= 0) {
                neiPoints.add(node.get(yLength * XSize + xLength - 1));
            }
            if ((yLength * XSize + xLength + 1) < node.size()) {
                neiPoints.add(node.get(yLength * XSize + xLength + 1));
            }
            if (((yLength + 1) * XSize + xLength) < node.size()) {
                neiPoints.add(node.get((yLength + 1) * XSize + xLength));
            }
            if (((yLength + 1) * XSize + xLength - 1) < node.size()) {
                neiPoints.add(node.get((yLength + 1) * XSize + xLength - 1));
            }
            if (((yLength + 1) * XSize + xLength + 1) < node.size()) {
                neiPoints.add(node.get((yLength + 1) * XSize + xLength + 1));
            }

            Double height = .0;
            int count = 0;
            for (Double[] elementPoint : neiPoints) {
                if (tools.IsPtInPoly(new Point2D.Double(elementPoint[0], elementPoint[1]), currentHexagon)) {
                    height += elementPoint[2];
                    count++;
                }
            }
            if (count != 0) {
                heightList.add(height / count);
                hexagoncoor.put("Z", height / count);
                if ((height / count) < demRange[4]) demRange[4] = height / count;
                if ((height / count) > demRange[5]) demRange[5] = height / count;
            } else {
                heightList.add(.0);
                hexagoncoor.put("Z", .0);
            }


        }


//        outPutData.put("height", heightList);
        outPutData.put("cellsize", cellLen);
        outPutData.put("range", demRange);

        return outPutData;
    }

    public static Map DemtoVoronoi(String DemPath) throws Exception {

        Map demInfor = gdalTool.getDemInf(DemPath);
        List<Double[]> node = (List<Double[]>) demInfor.get("node");
        List<Double[]> border = (List<Double[]>) demInfor.get("border");
        List<Map> mapBorder = new ArrayList<>();
        Double minX = border.get(0)[0];
        Double minY = border.get(0)[1];
        for (Double[] elementbord : border) {
            Map<String, Double> tempBorderP = new HashMap<>();
            if (minX > elementbord[0]) minX = elementbord[0];
            if (minY > elementbord[1]) minY = elementbord[1];
            tempBorderP.put("X", elementbord[0]);
            tempBorderP.put("Y", elementbord[1]);
            mapBorder.add(tempBorderP);
        }

//        voronoi填充
        double rasterLen = node.get(1)[0] - node.get(0)[0];
        double cellLen = rasterLen * 10;
        MyAlgo myAlgo = new MyAlgo(mapBorder, cellLen);

        Map outPutData = myAlgo.voronoi();
        List<Map> voronoi = (List<Map>) outPutData.get("element");
        int XSize = (int) demInfor.get("xsize");
        int YSize = (int) demInfor.get("ysize");
        List<Double> heightList = new ArrayList<>();

        Double[] voronoiRange = new Double[]{1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0};
        for (int i = 0; i < voronoi.size(); i++) {
            Map voronoicoor = voronoi.get(i);
            Double[] X = (Double[]) voronoicoor.get("X");
            Double[] Y = (Double[]) voronoicoor.get("Y");

            int startPX = (int) Math.floor((tools.MinofArray(X) - minX) / rasterLen);
            int startPY = (int) Math.floor((tools.MinofArray(Y) - minY) / rasterLen);

            if (startPX < 0) startPX = 0;
            if (startPY < 0) startPY = 0;

            int endPX = (int) Math.floor((tools.MaxofArray(X) - minX) / rasterLen);
            int endPY = (int) Math.floor((tools.MaxofArray(Y) - minY) / rasterLen);

            if (endPX < 0) endPX = 0;
            if (endPY < 0) endPY = 0;
//            当前单元
            List<Point2D.Double> currentElement = new ArrayList<>();
            for (int j = 0; j < X.length; j++) {
                currentElement.add(new Point2D.Double(X[j], Y[j]));
                //                获取最小最大的x，y
                if (X[j] < voronoiRange[0]) voronoiRange[0] = X[j];
                if (X[j] > voronoiRange[1]) voronoiRange[1] = X[j];

                if (Y[j] < voronoiRange[2]) voronoiRange[2] = Y[j];
                if (Y[j] > voronoiRange[3]) voronoiRange[3] = Y[j];

            }

            List<Double[]> neiPoints = new ArrayList<>();

            for (int j = startPY; j <= endPY; j++) {
                for (int k = startPX; k <= endPX; k++) {
                    if ((j * XSize + k) > node.size() || (j * XSize + k) < 0) {
                        System.out.println("Xp:" + k + " Yp:" + j);
                        continue;
                    }
                    neiPoints.add(node.get(j * XSize + k));
                }
            }


            Double height = .0;
            int count = 0;
            for (Double[] elementPoint : neiPoints) {
//                if (tools.IsPtInPoly(new Point2D.Double(elementPoint[0], elementPoint[1]), currentElement)) {
////                    height += elementPoint[2];
////                    count++;
////                }
                height += elementPoint[2];
                count++;
            }

            if (count != 0) {
                Double aveHeight = height / count;
                heightList.add(aveHeight);
                voronoicoor.put("Z", aveHeight);
                if (aveHeight < voronoiRange[4]) voronoiRange[4] = aveHeight;
                if (aveHeight > voronoiRange[5]) voronoiRange[5] = aveHeight;
            } else {
                heightList.add(.0);
                voronoicoor.put("Z", .0);
            }


        }


//        outPutData.put("height", heightList);
        outPutData.put("cellsize", cellLen);
        outPutData.put("range", voronoiRange);

        return outPutData;
    }

    public static Map DemtoTri(String DemPath) throws Exception {
        Map demInfor = gdalTool.getDemInf(DemPath);
        List<Double[]> node = (List<Double[]>) demInfor.get("node");
        List<Double[]> border = (List<Double[]>) demInfor.get("border");
        List<Map> mapBorder = new ArrayList<>();
        Double minX = border.get(0)[0];
        Double minY = border.get(0)[1];
        for (Double[] elementbord : border) {
            Map<String, Double> tempBorderP = new HashMap<>();
            if (minX > elementbord[0]) minX = elementbord[0];
            if (minY > elementbord[1]) minY = elementbord[1];
            tempBorderP.put("X", elementbord[0]);
            tempBorderP.put("Y", elementbord[1]);
            mapBorder.add(tempBorderP);
        }

        double rasterLen = node.get(1)[0] - node.get(0)[0];
        double cellLen = rasterLen * 100;

        //三角形填充
        gmsh mygmsh = new gmsh(mapBorder, cellLen);
        Map delatri = mygmsh.algoNonei("del2d");
        List<Map> tri = (List<Map>) delatri.get("element");


        int XSize = (int) demInfor.get("xsize");
        int YSize = (int) demInfor.get("ysize");

        List<Double> heightList = new ArrayList<>();

//        三角区域范围
        Double[] triRange = new Double[]{1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0, 1.0 / 0, -1.0 / 0};
        for (int i = 0; i < tri.size(); i++) {

            Map tricoor = tri.get(i);

            Double[] X = (Double[]) tricoor.get("X");
            Double[] Y = (Double[]) tricoor.get("Y");


            int startPX = (int) Math.floor((tools.MinofArray(X) - minX) / rasterLen);
            int startPY = (int) Math.floor((tools.MinofArray(Y) - minY) / rasterLen);

            int endPX = (int) Math.floor((tools.MaxofArray(X) - minX) / rasterLen);
            int endPY = (int) Math.floor((tools.MaxofArray(Y) - minY) / rasterLen);


//            当前三角形
            List<Point2D.Double> currentTri = new ArrayList<>();
            for (int j = 0; j < X.length; j++) {
                currentTri.add(new Point2D.Double(X[j], Y[j]));

                if (X[j] < triRange[0]) triRange[0] = X[j];
                if (X[j] > triRange[1]) triRange[1] = X[j];

                if (Y[j] < triRange[2]) triRange[2] = Y[j];
                if (Y[j] > triRange[3]) triRange[3] = Y[j];
            }

            List<Double[]> neiPoints = new ArrayList<>();

            for (int j = startPY; j <= endPY; j++) {
                for (int k = startPX; k <= endPX; k++) {
                    if ((j * XSize + k) > node.size()) {
                        System.out.println("Xp:" + k + " Yp:" + j);
                        continue;
                    }
                    neiPoints.add(node.get(j * XSize + k));
                }
            }


            Double height = .0;
            int count = 0;
            for (Double[] elementPoint : neiPoints) {
//                if (tools.IsPtInPoly(new Point2D.Double(elementPoint[0], elementPoint[1]), currentTri)) {
                height += elementPoint[2];
                count++;
//                }
            }
            if (count != 0) {
                heightList.add(height / count);
                tricoor.put("Z", height / count);

                if ((height / count) < triRange[4]) triRange[4] = height / count;
                if ((height / count) > triRange[5]) triRange[5] = height / count;

            } else {
                heightList.add(.0);
                tricoor.put("Z", .0);
            }
        }

        delatri.put("range", triRange);
        return delatri;
    }

    public static Map TriToQuads(String Algo) throws Exception {

        File file = new File("D:\\gmsh-4.6.0-Windows64\\temp.geo");
        FileWriter fileWriter = new FileWriter(file, true);
        PrintWriter printWriter = new PrintWriter(fileWriter);
        printWriter.println("Recombine Surface{1};\n");
        printWriter.flush();

        try {
            fileWriter.flush();
            printWriter.close();
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //四边形生成
        String command = "cmd /C " + gmshPath + "gmsh.exe " + gmshPath + "temp.geo -2 -algo \"" + Algo + "\" -o " + gmshPath + "temp.dat";
//        String command = "cmd /C cd E:\\网格研究\\gmsh-4.6.0-Windows64\\ & gmsh.exe temp.geo -2 -algo \"" + Algo + "\" -o temp.dat";
        Process p = Runtime.getRuntime().exec(command);
        p.waitFor();
        Map tempmap = File_operation.ReadFile("D:\\gmsh-4.6.0-Windows64\\temp.dat");
        List<Double[]> node = (List<Double[]>) tempmap.get("node");
        List<Integer[]> element = (List<Integer[]>) tempmap.get("element");
        List<Map> delatri = new ArrayList<>();
        for (int i = 0; i < element.size(); i++) {
            if (element.get(i).length < 4) continue;
            Map<String, Object> tempMap = new HashMap<String, Object>();
            double[] xout = new double[4];
            double[] yout = new double[4];
            xout[0] = node.get(element.get(i)[0] - 1)[0];
            xout[1] = node.get(element.get(i)[1] - 1)[0];
            xout[2] = node.get(element.get(i)[2] - 1)[0];
            xout[3] = node.get(element.get(i)[3] - 1)[0];

            yout[0] = node.get(element.get(i)[0] - 1)[1];
            yout[1] = node.get(element.get(i)[1] - 1)[1];
            yout[2] = node.get(element.get(i)[2] - 1)[1];
            yout[3] = node.get(element.get(i)[3] - 1)[1];

            tempMap.put("X", xout);
            tempMap.put("Y", yout);

            delatri.add(tempMap);

        }
        Map<String, Object> returndata = new HashMap<>();
        returndata.put("node", node);
        returndata.put("element", delatri);
        return returndata;
    }


    public static List<Double[]> CommonTransForm(List<Map> OriGrid, List<Map> TransGrid) {

        for (int i = 0; i < TransGrid.size(); i++) {
            Map transMap = TransGrid.get(i);
            Double[] X1 = (Double[]) transMap.get("X");
            Double[] Y1 = (Double[]) transMap.get("Y");

            List<Point2D.Double> trans = new ArrayList<>();
            for (int j = 0; j < X1.length; j++) {
                trans.add(new Point2D.Double(X1[j], Y1[j]));
            }

            int height = 0;
            for (int j = 0; j < OriGrid.size(); j++) {
                Map oriMap = OriGrid.get(i);
                Double[] X2 = (Double[]) oriMap.get("X");
                Double[] Y2 = (Double[]) oriMap.get("Y");

                for (int k = 0; k < X2.length; k++) {
                    trans.add(new Point2D.Double(X2[j], Y2[j]));
                    if (tools.IsPtInPoly(new Point2D.Double(X2[k], Y2[k]), trans)) {
                        height++;
                    }
                }

            }
        }
        return null;
    }
}
